/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/igfx/GfxInterface.h>
#include "../IGfx/GDILeak.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

namespace gfxinterface
{

UINT CVectorF::GetMinIndex() const
{
	UINT uSize = size();

	if (! uSize )
		return 0;

	UINT minimum = 0;
	float fMinimum = at(minimum);
	for (UINT i = 1; i<uSize; i++)
	{
		if ( fMinimum > at(i) )
		{
			minimum = i;
			fMinimum = at(minimum);
		}
	}

	return minimum;
}

UINT CVectorF::GetMaxIndex() const
{
	UINT uSize = size();

	if (! uSize )
		return 0;

	UINT maximum = 0;
	float fMaximum = at(maximum);
	for (UINT i = 1; i<uSize; i++)
	{
		if ( fMaximum < at(i) )
		{
			maximum = i;
		    fMaximum = at(maximum);
		}
	}

	return maximum;
}


void CVectorF::GetMinMaxIndex(UINT& uMin, UINT& uMax) const
{
	UINT uSize = size();

	if (! uSize )
		return;

	uMin = uMax = 0;
	float fMin=at(uMin), fMax=at(uMax);
	float at_i;
	for (UINT i = 1; i<uSize; i++)
	{
		at_i = at(i);
		if ( fMin > at_i )
		{
			uMin = i;
			fMin=at(uMin);
		}
		if ( fMax < at_i )
		{
			uMax = i;
			fMax=at(uMax);
		}
	}
}

CGfxInterface::CGfxInterface()
{
	m_rCurClip.left=m_rCurClip.top=m_rCurClip.width=m_rCurClip.height=0;
	m_sCurBBox.screen.left=0;
	m_sCurBBox.screen.top=m_sCurBBox.screen.width=m_sCurBBox.screen.height=1;
	m_sCurBBox.world.left=0;
	m_sCurBBox.world.top=m_sCurBBox.world.width=m_sCurBBox.world.height=1;

	m_dFontRatio=1;
	m_hDC=NULL;

	m_dwRenderOption = 0;
}

CGfxInterface::~CGfxInterface()
{
	SetHDC(NULL);
}

void CGfxInterface::BeginPaint(HDC hDC)
{
	if (hDC)
	{
		m_hDC = hDC;
		m_bDeleteDC=false;
	}
	else
	{
		m_hDC = ::CreateDC( _T("DISPLAY"),NULL,NULL,NULL);
		m_bDeleteDC=true;
	}

	m_sCurBBox.screen=m_rMainBoundingBox;
	m_sCurBBox.world.left=0;
	m_sCurBBox.world.top=1;
	m_sCurBBox.world.width=m_sCurBBox.world.height=1;

	m_rCurClip.left=(double)m_rMainBoundingBox.left;
	m_rCurClip.top=(double)m_rMainBoundingBox.top;
	m_rCurClip.width=(double)m_rMainBoundingBox.width;
	m_rCurClip.height=(double)m_rMainBoundingBox.height;
}

void CGfxInterface::EndPaint()
{
	m_stClips=SRectDStack();
	m_stBBoxes=SBBoxStateStack();
	m_sCurBBox.screen= m_rMainBoundingBox;
	SetHDC(NULL);
}

bool CGfxInterface::SetMainBoundingBox(const CRect& rect)
{
	m_rMainBoundingBox.left=rect.left;
	m_rMainBoundingBox.top=rect.top;
	m_rMainBoundingBox.width=rect.Width();
	m_rMainBoundingBox.height=-rect.Height();

	return SetBoundingBox(m_rMainBoundingBox.left, m_rMainBoundingBox.top - m_rMainBoundingBox.height,
		m_rMainBoundingBox.left + m_rMainBoundingBox.width, m_rMainBoundingBox.top);
}

bool CGfxInterface::SetMainBoundingBox(int llx, int lly, int urx, int ury)
{
	if ((llx>=urx) || (lly>=ury))
		return false;

	m_rMainBoundingBox.left=llx;
	m_rMainBoundingBox.top=ury;
	m_rMainBoundingBox.width=urx-llx;
	m_rMainBoundingBox.height=ury-lly;
	if(m_rMainBoundingBox.width<0)
        throw std::exception("MainBoundingBox.Width must be > 0");
    if(m_rMainBoundingBox.height<0)
        throw std::exception("MainBoundingBox.Width must be > 0");

	return SetBoundingBox(llx,lly,urx,ury);
}

bool CGfxInterface::SetBoundingBox(int llx, int lly, int urx, int ury)
{
	if ((llx>=urx) || (lly>=ury))
		return false;

	m_sCurBBox.screen.left=llx;
	m_sCurBBox.screen.top=ury;
	m_sCurBBox.screen.width=urx-llx;
	m_sCurBBox.screen.height=ury-lly;
    if(m_sCurBBox.screen.width<0)
        throw std::exception("CurBoundingBox.Width must be > 0");
    if(m_sCurBBox.screen.height<0)
        throw std::exception("CurBoundingBox.Width must be > 0");

	return true;
}

bool CGfxInterface::SetWorldBoundingBox(double llx, double lly, double urx, double ury)
{
	if ((llx>=urx) || (lly>=ury))
		return false;

	m_sCurBBox.world.left=llx;
	m_sCurBBox.world.top=ury;
	m_sCurBBox.world.width=urx-llx;
	m_sCurBBox.world.height=ury-lly;

	return true;
}



void CGfxInterface::GetDefaultLOGFONT(LOGFONT& lf)
{
	lf.lfHeight=8;
	lf.lfWidth=0;
	lf.lfEscapement=0;
	lf.lfOrientation=0;
	lf.lfWeight=FW_NORMAL;
	lf.lfItalic=FALSE;
	lf.lfUnderline=FALSE;
	lf.lfStrikeOut=FALSE;
	lf.lfCharSet=ANSI_CHARSET;
	lf.lfOutPrecision=OUT_TT_PRECIS;
	lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
	lf.lfQuality=ANTIALIASED_QUALITY;
	lf.lfPitchAndFamily=FF_DONTCARE|DEFAULT_PITCH;
}


int CGfxInterface::CreateFontSize(HDC hDC, int points)
{
	// This will calculate the font size for the printer that is specified
	// by a point size.
	//
	// if points is:
	//  (-) negative uses height as value for Net Font Height (ie. point size)
	//	(+) positive height is Total Height plus Leading Height!
	if(!hDC)
        throw std::exception("HDC is null");
	
	POINT size;
   int logPixels = ::GetDeviceCaps(hDC, LOGPIXELSY);
	size.x = size.y = MulDiv(points, logPixels, 72);

//	::DPtoLP(hDC, &size, 1);

	return (int)floor(abs(size.y)*m_dFontRatio);
}

SPointD CGfxInterface::MeasureString(LPCTSTR szString, LOGFONT lf)
{
	using namespace Gdiplus;

	USES_CONVERSION;
	LPTSTR szLine;
	SPointD pointSize;
	size_t len,linelen;

	if (!m_hDC)
	{
		m_bDeleteDC=true;
		m_hDC = ::CreateDC(_T("DISPLAY"),NULL,NULL,NULL);
	}

	if(!m_hDC)
        throw std::exception("HDC is null");

  
	lf.lfHeight=-(long)floor(CreateFontSize(m_hDC, lf.lfHeight)/m_dFontRatio);
  
	Font font(m_hDC, &lf);
	if (font.GetLastStatus()==Gdiplus::Ok)
	{
		Graphics graphics(m_hDC);
		RectF bbox;
		graphics.MeasureString( T2CW(szString), -1, &font, PointF(0,0), &bbox);
      // Done with the HDC. Clear it out
      SetHDC(NULL);
      return SPointD( bbox.Width, bbox.Height);
	}
	
   // old method
	HFONT hFont=CreateFontIndirect(&lf);
	ASSERT(hFont);
	HGDIOBJ pOldObject=SelectObject(m_hDC, hFont);
	SIZE size;

	pointSize.x=pointSize.y=0;
	len=_tcslen(szString);
	while (len>0)
	{
		szLine=LPTSTR(_tcschr(szString,'\n'));
		if (szLine)
			linelen=szLine-szString;
		else
			linelen=len;

		VERIFY(GetTextExtentPoint32(m_hDC, szString, linelen , &size));
		szString+=linelen;

		if (linelen!=len)
			szString++;

		len=_tcslen(szString);
		pointSize.x=__max(size.cx, pointSize.x);
		pointSize.y+=size.cy;
	}

	VERIFY(SelectObject(m_hDC, pOldObject));
	VERIFY(DeleteObject(hFont));


	return pointSize;
}

void CGfxInterface::SetHDC(HDC hDC)
{
	if (m_bDeleteDC && m_hDC)
		::DeleteDC(m_hDC);
	
	m_hDC = hDC;
	m_bDeleteDC = false;
}

} // End of namespace
